import { BaseObject } from "./object";
import { EventDispatcher } from "./event";
/**
 * 事件枚举。
 */
export enum EventType {
    HistoryState,
    HistoryAdd,
    HistoryFree
}
/**
 * isUndo 是否撤销
 * data 状态自定义参数
 */
export type EventData<D> = { isUndo: boolean, data: D };
/**
 * 用于扩展的历史状态接口。
 */
export interface IState {
    /**
     * 执行此状态时是否需要触发事件，并做为事件自定义参数。
     */
    data: any;
    /**
     * 撤销。
     */
    undo(): boolean;
    /**
     * 执行。
     */
    redo(): boolean;
}
/**
 * 历史状态基类。
 */
export abstract class BaseState extends BaseObject implements IState {
    /**
     * 执行此状态时是否需要触发事件，并做为事件自定义参数。
     */
    public data: any = null;
    /**
     * 防止重复执行。
     */
    private _isDone: boolean = false;

    protected _onClear(): void {
    }
    /**
     * 撤销。
     */
    public undo(): boolean {
        if (this._isDone) {
            this._isDone = false;

            return true;
        }

        return false;
    }
    /**
     * 执行。
     */
    public redo(): boolean {
        if (this._isDone) {
            return false;
        }

        this._isDone = true;

        return true;
    }
}
/**
 * 历史记录。
 */
export class History extends BaseState {
    public static toString(): string {
        return "[class History]";
    }
    /**
     * 事件生成器，当历史状态激活时，触发事件。
     */
    public dispatcher: EventDispatcher | null = null;
    /**
     * 当历史记录正在变更时，防止因外部逻辑错误破坏历史记录。
     * 未锁，锁定，锁定，锁定
     */
    private _locked: 0 | 1 | 2 | 3 = 0;
    /**
     * 历史记录当前的位置。
     */
    private _index: number = -1;
    /**
     * 状态列表。
     */
    private _states: IState[] = [];
    /**
     * 批次列表。
     */
    private _batchs: History[] = [];
    /**
     * 当历史记录在中间执行出现分支时，废弃前一个分支。
     */
    private _free(): void {
        if (this._states.length > this._index + 1) {
            this._states.length = this._index + 1;
            if (this.dispatcher !== null) {
                this.dispatcher.dispatchEventWidth(EventType[EventType.HistoryFree], null);
            }
        }
    }
    /**
     * 执行或撤销状态。
     */
    private _doState(state: IState, isUndo: boolean): void {
        if (isUndo) {
            state.undo();
        }
        else {
            state.redo();
        }

        if (this.dispatcher !== null && state.data) {
            const data: EventData<any> = { isUndo: isUndo, data: state.data };
            this.dispatcher.dispatchEventWidth(EventType[EventType.HistoryState], data);
        }
    }
    /**
     * 
     */
    private _getStateByObject(history: History, object: any, key: string, link: IState | null = null): ModifyObjectState | null {
        let i = history._states.length;
        while (i--) {
            const state = history._states[i];
            if (state instanceof ModifyObjectState) {
                if ((link === null || state !== link) && state.object === object && state.key === key) {
                    return state;
                }
            }
            else if (state instanceof History) {
                const subState = this._getStateByObject(state, object, key, link);
                if (subState !== null) {
                    return subState;
                }
            }
        }

        return null;
    }
    /**
     * 撤销到历史头。
     */
    public undo(): boolean {
        if (this._batchs.length > 0) {
            return false;
        }

        return this.go(-1);
    }
    /**
     * 执行到历史尾。
     */
    public redo(): boolean {
        if (this._batchs.length > 0) {
            return false;
        }

        return this.go(this._states.length - 1);
    }
    /**
     * 回退一个状态。
     */
    public back(): boolean {
        if (this._index < 0) {
            return false;
        }

        if (this._batchs.length > 0) {
            return false;
        }

        this._locked |= 1;
        this._doState(this._states[this._index--], true);
        this._locked &= 2;

        return true;
    }
    /**
     * 前进一个状态。
     */
    public forward(): boolean {
        if (this._index >= this._states.length - 1) {
            return false;
        }

        if (this._batchs.length > 0) {
            return false;
        }

        this._locked |= 1;
        this._doState(this._states[++this._index], false);
        this._locked &= 2;

        return true;
    }
    /**
     * 跳转到指定状态。
     */
    public go(index: number): boolean {
        if (this._batchs.length > 0) {
            return false;
        }

        let result = false;

        if (this._index < index) {
            while (this._index !== index && this.forward()) {
                result = true;
            }
        }
        else {
            while (this._index !== index && this.back()) {
                result = true;
            }
        }

        return result;
    }
    /**
     * 添加并执行状态。
     */
    public add(state: IState): void {
        if (this._locked !== 0) {
            return;
        }

        if (this._batchs.length > 0) { // 批次添加中。
            const batch = this._batchs[this._batchs.length - 1];
            batch.add(state);
        }
        else {
            this._states[this._index + 1] = state;
            if (this.dispatcher !== null) {
                this.dispatcher.dispatchEventWidth(EventType[EventType.HistoryAdd], state);
            }

            this.forward();
            this._free();
        }
    }
    /**
     * 添加并批次。
     */
    public addBatch(...args: IState[]): History | null {
        if (this._locked !== 0) {
            return null;
        }

        const batch = History.create(History);
        batch.dispatcher = this.dispatcher;

        for (const state of args) {
            batch.add(state);
        }

        this.add(batch);

        return batch;
    }
    /**
     * 开始批次。
     */
    public beginBatch(): History {
        const batch = History.create(History);
        this._batchs.push(batch);

        return batch;
    }
    /**
     * 结束批次。
     */
    public endBatch(data: any = null) {
        if (this._batchs.length < 1) { // 已经没有进行的批次。
            return;
        }

        const batch = this._batchs.pop() as History;
        if (batch.count < 1) { // 空批次。
            return;
        }

        batch.dispatcher = this.dispatcher;
        batch.data = data;
        batch._locked |= 2;

        if (this._batchs.length > 0) {
            this._batchs[this._batchs.length - 1].add(batch);
        }
        else {
            this.add(batch);
        }
    }
    /**
     * 
     */
    public linkObjectState(object: any, key: string): void {
        const currentState = this._getStateByObject(this, object, key);
        if (currentState !== null) {
            const prevState = this._getStateByObject(this, object, key, currentState);
            if (prevState !== null) {
                currentState.fromValue = prevState.toValue;
            }
        }
    }
    /**
     * 获取指定状态。
     */
    public getState(index: number): IState | null {
        return this._states[index];
    }
    /**
     * 当前指定标识。
     */
    public get locked(): boolean {
        return this._locked !== 0;
    }
    public set locked(value: boolean) {
        if (value) {
            this._locked |= 1;
        }
        else {
            this._locked &= 2;
        }
    }
    /**
     * 当前指定标识。
     */
    public get index(): number {
        return this._index;
    }
    /**
     * 总状态数。
     */
    public get count(): number {
        return this._states.length;
    }
}

export class ModifyObjectState extends BaseState {
    public static createState(object: any, key: number | string, value: any, data: any = null): ModifyObjectState | null {
        const state = new ModifyObjectState();
        state.object = object;
        state.key = key;
        state.data = data;
        state.fromValue = (state.key in state.object) ? state.object[state.key] : undefined;
        state.toValue = value;

        if (state.fromValue === state.toValue) {
            return null;
        }

        return state;
    }

    public object: any;
    public key: number | string;
    public fromValue: any = null;
    public toValue: any = null;

    public undo(): boolean {
        if (super.undo()) {
            if (this.fromValue !== undefined) {
                this.object[this.key] = this.fromValue;
            }
            else {
                delete this.object[this.key];
            }

            return true;
        }

        return false;
    }

    public redo(): boolean {
        if (super.redo()) {
            if (this.toValue !== undefined) {
                this.object[this.key] = this.toValue;
            }
            else {
                delete this.object[this.key];
            }

            return true;
        }

        return false;
    }
}

export class ModifyArrayState extends BaseState {
    public static createState(array: any[], index: number, value: any, data: any = null): ModifyArrayState {
        if (index < 0) {
            if (array.length > 0) {
                index = array.length - 1;
            }
            else {
                index = 0;
            }
        }

        const state = new ModifyArrayState();
        state.array = array;
        state.index = index;
        state.data = data;
        if (value) {
            state.toValue = value;
        }
        else {
            state.fromValue = state.array[state.index];
        }

        return state;
    }

    public array: any[];
    public index: number = -1;
    public fromValue: any = null;
    public toValue: any = null;

    public undo(): boolean {
        if (super.undo()) {
            if (this.fromValue) {
                this.array.splice(this.index, 0, this.fromValue);
            }
            else {
                this.array.splice(this.index, 1);
            }

            return true;
        }

        return false;
    }

    public redo(): boolean {
        if (super.redo()) {
            if (this.toValue) {
                this.array.splice(this.index, 0, this.toValue);
            }
            else {
                this.array.splice(this.index, 1);
            }

            return true;
        }

        return false;
    }
}